package com.example.ccj.u.aliyun.oss;



import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.OSS;
import com.alibaba.sdk.android.oss.OSSClient;
import com.alibaba.sdk.android.oss.ServiceException;
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback;
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback;
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider;
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider;
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask;
import com.alibaba.sdk.android.oss.model.DeleteObjectRequest;
import com.alibaba.sdk.android.oss.model.DeleteObjectResult;
import com.alibaba.sdk.android.oss.model.HeadObjectRequest;
import com.alibaba.sdk.android.oss.model.HeadObjectResult;
import com.alibaba.sdk.android.oss.model.ObjectMetadata;
import com.alibaba.sdk.android.oss.model.PutObjectRequest;
import com.alibaba.sdk.android.oss.model.PutObjectResult;
import com.example.ccj.u.ActivityUtils;
import com.example.ccj.u.GenerateId;
import com.example.ccj.u.LogUtils;
import com.example.ccj.u.StringUtils;
import com.luck.picture.lib.entity.LocalMedia;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * OSS工具
 * 需要在阿里云配置STS临时访问凭证访问OSS获取accessKeyId 、accessKeySecret 、 securityToken
 * 获取后将其值赋给  赋值一次即可
 * 已经配置好的服务器地址是：/oss/getOssSTS
 */
public class OssUtils {
    private final static String TAG = "OssUtil";

    public static STS sts;

    // 从STS服务获取的临时访问密钥（AccessKey ID和AccessKey Secret）。
    private static String accessKeyId = null;
    private static String accessKeySecret = null;
    //存储桶
    public static String BUCKET = null;
    // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1（杭州）为例，Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
    public static String endpoint = "oss-cn-beijing.aliyuncs.com";
    // 从STS服务获取的安全令牌（SecurityToken）。
    private static String securityToken = null;

    /**
     * 初始化OSSClient
     * @return OSSClient对象
     */
    private static OSSClient getOSSClient(){
        if (sts == null) {
            LogUtils.eTag(TAG,"未配获取到Sts配置，请检查");
            throw new RuntimeException("OSS调用，未配获取到Sts配置，请检查");
        }

        accessKeyId = sts.getAccessKeyId();
        accessKeySecret = sts.getAccessKeySecret();
        securityToken = sts.getSecurityToken();

        if (accessKeyId == null) {
            LogUtils.eTag(TAG,"未配置 accessKeyId，请先给 OssUtils.accessKeyId 赋值");
            throw new RuntimeException("OSS调用，未配置 accessKeyId，请先通过STS获取服务器的accessKeyId，然后给OssUtils.accessKeyId 赋值");
        }

        if (accessKeySecret == null) {
            LogUtils.eTag(TAG,"未配置 AccessKeySecret，请先给 OssUtils.accessKeySecret 赋值");
            throw new RuntimeException("OSS调用，未配置 AccessKeySecret，请先通过STS获取服务器的 accessKeySecret，然后给 OssUtils.accessKeySecret 赋值");
        }

        if (BUCKET == null) {
            LogUtils.eTag(TAG,"未配置 BUCKET，请先给 OssUtils.BUCKET 赋值,或者传入");
            throw new RuntimeException("OSS调用，未配置 BUCKET，请先通过STS获取服务器的 BUCKET，然后给 OssUtils.BUCKET 赋值");
        }

        if (securityToken == null) {
            LogUtils.eTag(TAG,"未配置 securityToken，请先给 OssUtils.securityToken 赋值,或者传入");
            throw new RuntimeException("OSS调用，未配置 securityToken，请先通过STS获取服务器的 securityToken，然后给 OssUtils.securityToken 赋值");
        }

        OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(accessKeyId, accessKeySecret, securityToken);
        // 创建OSSClient实例。
        return new OSSClient(ActivityUtils.getTopActivity(), endpoint, credentialProvider);
    }

    /**
     * 获取文件访问链接
     * @param objectKey 文件名
     */
    public static String getObjectUrl(String objectKey){
        return "https://"+BUCKET+"."+endpoint+"/"+objectKey;
    }

    /**
     * 获取文件访问链接
     * @param objectKey 文件名
     */
    public static String getObjectUrl(String objectKey, String bucket){
        return "https://"+bucket+"."+endpoint+"/"+objectKey;
    }


    /**
     * 文件是否存在
     * @param objectKey 文件名
     * @return 是否存在
     * @throws ServiceException 服务异常
     * @throws ClientException  请求异常
     */
    public static boolean fileIsExist(String objectKey, String objectPath) throws ServiceException, ClientException {
        //文件名地址是否为空
        if (StringUtils.isEmpty(objectKey,objectPath)) {
            LogUtils.e(TAG, "fileIsExist() oss上传文件文件名及文件地址不可为空: " + objectKey);
            return true;
        }

        //oss文件是否存在
        OSS oss = getOSSClient();
        if (oss == null) {
            return false;
        }
        boolean b = oss.doesObjectExist(BUCKET, objectKey);
        if (b) LogUtils.d(TAG, "taskPutOneFile() 上传单个文件，文件已存在，取消上传: " + objectKey);
        return b;
    }


    /**
     * 获取文件大小
     * @param objectKey 文件名
     * @return 长度byte
     */
    public static long getObjectSize(String objectKey) throws ServiceException, ClientException {
        OSS oss = getOSSClient();
        if (oss == null) {
            return 0;
        }
        HeadObjectResult headObjectResult = oss.headObject(new HeadObjectRequest(BUCKET, objectKey));
        return headObjectResult.getMetadata().getContentLength();
    }

    public static String syncPutFile(List<LocalMedia> localMediaList, String tag,IOssCallBack ossCallBack) {
        Map<String,String> pMap = new HashMap<>();
        StringBuilder stringBuilder = new StringBuilder();
        for (LocalMedia localMedia : localMediaList) {
            String[] split = localMedia.getFileName().split("\\.");
            String noRepeatTimeIdToStr = GenerateId.getNoRepeatTimeIdToStr();
            noRepeatTimeIdToStr += "." + split[1];
            pMap.put(noRepeatTimeIdToStr,localMedia.getRealPath());
            stringBuilder.append(OssUtils.getObjectUrl(noRepeatTimeIdToStr)).append(",");
        }
        syncPutFile(pMap,BUCKET,tag,null,ossCallBack);
        return stringBuilder.toString();
    }

    public static void syncPutFile(Map<String,String> fileMap,  IOssCallBack ossCallBack){
        syncPutFile(fileMap,BUCKET,null,null,ossCallBack);
    }

    public static void syncPutFile(Map<String,String> fileMap, String tag, IOssCallBack ossCallBack){
        syncPutFile(fileMap,BUCKET,tag,null,ossCallBack);
    }

    public static void syncPutFile(Map<String,String> fileMap, OssFileType fileType, IOssCallBack ossCallBack){
        syncPutFile(fileMap,BUCKET,null,fileType,ossCallBack);
    }

    public static void syncPutFile(Map<String,String> fileMap, String tag, OssFileType fileType, IOssCallBack ossCallBack){
        syncPutFile(fileMap,BUCKET,tag,fileType,ossCallBack);
    }

    /**
     * 异步多文件上传
     * @param fileMap 文件集合，key是存储到OSS的文件名，value是本地文件的地址
     * @param bucket 存储桶
     * @param tag 标签
     * @param fileType 文件类型
     * @param ossCallBack 回调
     */
    public static void syncPutFile(Map<String,String> fileMap, String bucket, String tag, OssFileType fileType, IOssCallBack ossCallBack){
        tag = getTag(tag,fileType);
        final int[] i = {0};

        for (String key: fileMap.keySet()) {
            String fileUri = fileMap.get(key);
            if (StringUtils.isEmpty(fileUri)) {
                LogUtils.e(TAG,key+"-文件本地地址为空取消OSS上传");
                ossCallBack.error(key+"-文件本地地址为空取消OSS上传");
                i[0]++;
                continue;
            }

            syncPutOneFile(key, fileUri, bucket, tag, new IOssCallBack() {
                @Override
                public void start(OSSAsyncTask<PutObjectResult> task) {
                    ossCallBack.start(task);
                }

                @Override
                public void error(String errorStr) {
                    ossCallBack.error(errorStr);
                }

                @Override
                public void success(String fileName, String ETag, String RequestId,int id) {
                    ossCallBack.success(fileName,ETag,RequestId,i[0]++);
                    if (i[0] == fileMap.size()) ossCallBack.allSuccess();
                }

                @Override
                public void speed(String fileName, int speed) {
                    int i1 = i[0] * 100 + speed;
                    ossCallBack.speed(fileName,i1);
                }
            });

        }
    }


    public static void syncPutFile(String objectKey,String fileUri, IOssCallBack ossCallBack){
        syncPutOneFile(objectKey,fileUri,BUCKET,null,ossCallBack);
    }

    public static void syncPutFile(String objectKey,String fileUri,OssFileType fileType, IOssCallBack ossCallBack){
        String tag = getTag(null,fileType);
        syncPutOneFile(objectKey,fileUri,BUCKET,tag,ossCallBack);
    }

    /**
     * 异步上传单个文件
     * @param objectKey OSS存储文件名
     * @param fileUri 本地文件地址
     * @param bucket 存储桶
     * @param tag 标记
     * @param ossCallBack 回调
     */
    public static void syncPutFile(String objectKey,String fileUri,String bucket,String tag,OssFileType fileType, IOssCallBack ossCallBack){
        tag = getTag(tag,fileType);
        syncPutOneFile(objectKey,fileUri,bucket,tag,ossCallBack);
    }

    //获取标签
    private static String getTag(String tag,OssFileType fileType){
        String prefixTag = null;
        if (fileType != null) {
            switch (fileType) {
                case img:
                    prefixTag = "img";
                    break;
                case text:
                    prefixTag = "text";
                    break;
                case excel:
                    prefixTag = "excel";
                    break;
                case word:
                    prefixTag = "word";
                    break;
                case other:
                    prefixTag = "other";
                    break;
                case video:
                    prefixTag = "video";
                    break;
                case dwg:
                    prefixTag = "dwg";
                    break;
            }
        }
        if (tag == null && prefixTag == null) return null;

        if (tag == null) return prefixTag;

        if (prefixTag == null) return tag;

        return prefixTag + "," + tag;
    }

    /**
     * 异步上传
     * @param objectKey OSS存储文件名
     * @param fileUri 本地文件地址
     * @param bucket 存储桶
     * @param tag 标记
     * @param ossCallBack 回调
     */
    private static void syncPutOneFile(String objectKey,String fileUri,String bucket,String tag,IOssCallBack ossCallBack){
        if (bucket != null) BUCKET = bucket;
        OSSClient oss = getOSSClient();
        if (oss == null) return;

        // 构造上传请求。
        // 依次填写Bucket名称（例如examplebucket）、Object完整路径（例如exampledir/exampleobject.txt）和本地文件完整路径（例如/storage/emulated/0/oss/examplefile.txt）。
        // Object完整路径中不能包含Bucket名称。
        PutObjectRequest put = new PutObjectRequest(BUCKET, objectKey,fileUri);

        // 设置文件元数据为可选操作。
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setHeader("x-oss-storage-class", "Standard");
        // 设置禁止覆盖同名Object。
        metadata.setHeader("x-oss-forbid-overwrite", "true");
        // 指定Object的对象标签，可同时设置多个标签。
        metadata.setHeader("x-oss-tagging", tag);

        // 异步上传时可以设置进度回调。
        put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
            @Override
            public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
                double progress = ((float) currentSize / (float)totalSize) * 100 ;
                ossCallBack.speed(objectKey,(int) progress);
            }
        });

        OSSAsyncTask<PutObjectResult> task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
            @Override
            public void onSuccess(PutObjectRequest request, PutObjectResult result) {
                LogUtils.d(TAG,"文件" + objectKey + "-上传完毕。"+result.getRequestId());
                ossCallBack.success(objectKey,result.getETag(),result.getRequestId(),0);
            }

            @Override
            public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
                // 请求异常。
                if (clientExcepion != null) {
                    // 客户端异常，例如网络异常等。
                    LogUtils.e(TAG,"OSS上传文件" + objectKey + "-OSS客户端异常，网络异常:"+clientExcepion.getMessage());
                    ossCallBack.error( objectKey +" OSS客户端异常，网络异常:" + clientExcepion.getMessage());
                }
                if (serviceException != null) {
                    // 服务端异常。
                    String errorStr = "RequestId:"+serviceException.getRequestId()+
                            "-ErrorCode:"+serviceException.getErrorCode()+
                            "-HostId:"+serviceException.getHostId()+
                            "-RawMessage:"+serviceException.getRawMessage();
                    LogUtils.e(TAG,"OSS上传文件:" + objectKey + "-OSS服务端异常:"+errorStr);
                    ossCallBack.error( objectKey +" 服务端异常:" + serviceException.getMessage());
                }
            }
        });

        //开始上传回调
        ossCallBack.start(task);
// 取消上传任务。
// task.cancel();
// 等待上传任务完成。
    task.waitUntilFinished();

    }





    /**
     * 同步上传单个文件 需要在子线程中执行
     * @param objectKey 文件名
     * @param fileUri 本地文件地址
     * @param bucket OSS存储桶  可为空
     * @param tag 标签  可为空
     */
    private static void putOneFile(String objectKey,String fileUri,String bucket,String tag,IOssCallBack ossCallBack) {
        if (bucket != null) BUCKET = bucket;
        OSSClient ossClient = getOSSClient();
        if (ossClient == null) return;
        // 构造上传请求。
        // 依次填写Bucket名称（例如examplebucket）、Object完整路径（例如exampledir/exampleobject.txt）和本地文件完整路径（例如/storage/emulated/0/oss/examplefile.txt）。
        // Object完整路径中不能包含Bucket名称。
        PutObjectRequest put = new PutObjectRequest(BUCKET, objectKey,fileUri);

        // 设置文件元数据为可选操作。
        ObjectMetadata metadata = new ObjectMetadata();
        // metadata.setContentType("application/octet-stream"); // 设置content-type。
        // metadata.setContentMD5(BinaryUtil.calculateBase64Md5(uploadFilePath)); // 校验MD5。
        // 设置Object的访问权限为私有。
//        metadata.setHeader("x-oss-object-acl", "private");
        // 设置Object的存储类型为标准存储。
        metadata.setHeader("x-oss-storage-class", "Standard");
        // 设置禁止覆盖同名Object。
         metadata.setHeader("x-oss-forbid-overwrite", "true");
        // 指定Object的对象标签，可同时设置多个标签。
         metadata.setHeader("x-oss-tagging", tag);
        // 指定OSS创建目标Object时使用的服务器端加密算法 。
        // metadata.setHeader("x-oss-server-side-encryption", "AES256");
        // 表示KMS托管的用户主密钥，该参数仅在x-oss-server-side-encryption为KMS时有效。
        // metadata.setHeader("x-oss-server-side-encryption-key-id", "9468da86-3509-4f8d-a61e-6eab1eac****");

        // 设置进度回调函数打印进度条。
        put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
            @Override
            public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
                double progress = ((float) currentSize / (float)totalSize) * 100 ;
                ossCallBack.speed(objectKey,(int) progress);
            }
        });

        put.setMetadata(metadata);

        try {
            PutObjectResult putResult = ossClient.putObject(put);
            LogUtils.d(TAG,"文件" + objectKey + "-上传完毕。"+putResult.getRequestId());
            ossCallBack.success(objectKey,putResult.getETag(),putResult.getRequestId(),0);
        } catch (ClientException e) {
            // 客户端异常，例如网络异常等。
            LogUtils.e(TAG,"OSS上传文件" + objectKey + "-OSS客户端异常，网络异常:"+e.getMessage());
            ossCallBack.error( objectKey +" OSS客户端异常，网络异常:" + e.getMessage());
        } catch (ServiceException e) {
            // 服务端异常。
            String errorStr = "RequestId"+e.getRequestId()+
                    "ErrorCode"+e.getErrorCode()+
                    "HostId"+e.getHostId()+
                    "RawMessage"+e.getRawMessage();
            LogUtils.e(TAG,"OSS上传文件:" + objectKey + "-OSS服务端异常:"+errorStr);
            ossCallBack.error( objectKey +" 服务端异常:" + e.getMessage());
        }

    }


    public static void deleteOneFile(String bucket,String fileName,IOssCallBack callBack){
        OSSClient ossClient = getOSSClient();
        if (bucket != null) BUCKET = bucket;
        // 创建删除请求。
        // 依次填写Bucket名称（例如examplebucket）和Object完整路径（例如exampledir/exampleobject.txt）。Object完整路径中不能包含Bucket名称。
        DeleteObjectRequest delete = new DeleteObjectRequest(BUCKET, fileName);
        // 异步删除。
        OSSAsyncTask deleteTask = ossClient.asyncDeleteObject(delete, new OSSCompletedCallback<DeleteObjectRequest, DeleteObjectResult>() {
            @Override
            public void onSuccess(DeleteObjectRequest request, DeleteObjectResult result) {
                callBack.allSuccess();
                LogUtils.d("asyncDeleteObject", "success!");
            }

            @Override
            public void onFailure(DeleteObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
                // 请求异常。
                if (clientExcepion != null) {
                    // 客户端异常，例如网络异常等。
                    clientExcepion.printStackTrace();
                    callBack.error("OSS删除文件网络异常:"+clientExcepion.getMessage());
                }
                if (serviceException != null) {
                    // 服务端异常。
                    callBack.error("OSS删除文件服务端异常:"+serviceException.getRawMessage());
                    LogUtils.e("ErrorCode", serviceException.getErrorCode());
                    LogUtils.e("RequestId", serviceException.getRequestId());
                    LogUtils.e("HostId", serviceException.getHostId());
                    LogUtils.e("RawMessage", serviceException.getRawMessage());
                }
            }
        });

    }





}
